//百度地图SDK：http://lbsyun.baidu.com/index.php?title=jspopular
const {BMap} = window;

//默认的颜色
const color = {
  line: '#1AC68C', //线覆盖物的颜色
  polygonFill: 'white', //多边形覆盖物的填充色
  polygonStroke: '#B0B0AF' //多边形覆盖物的边框色
};

/**
 * 基于百度地图的工具类：将异步操作使用Promise封装
 */
const MapTool = {
  //百度地图的密钥
  ak: 'ZAhBoB7sh8v50s6WoSU0KmhKwZ8G7MrP',
  //百度地图默认缩放级别
  defaultZoom: 14,

  /**
   * 转换为百度的Point
   * @param point 经度或坐标，格式：string或{lng: Number, lat: Number}
   */
  getPoint(point) {
    if (typeof point === 'string') {
      return point;
    } else if (typeof point === 'object') {
      if (point instanceof BMap.Point) {
        return point;
      }
      return new BMap.Point(point.lng, point.lat);
    }
  },

  /**
   * 本地搜索
   * 注意：如果在LocalSearch的构造函数中设置onSearchComplete，那么onSearchComplete就是固定的，所以第二次搜索时，Promise的resolve不会调用。
   * promise中初始化searchLocalB，那么第二次搜索时，Promise没有返回值
   * @param map 百度地图控件
   * @param place 需要搜索的地方
   */
  searchLocal(map, place) {
    this.searchLocalB = this.searchLocalB || new BMap.LocalSearch(map);
    return new Promise((resolve, reject) => {
      let onSearchComplete = (result) => {
        if (result) {
          let point = result.getPoi(0).point;
          resolve(point);
        } else {
          reject(result);
        }
      };
      this.searchLocalB.setSearchCompleteCallback(onSearchComplete);
      this.searchLocalB.search(place);
    });
  },

  /**
   * 智能搜索
   * @param map 百度地图控件
   * @param input 输入框ID
   * @param place 需要搜索的地方
   */
  searchAuto(map, input, place = '') {
    let option = {location: map, input};
    this.searchAutoB = this.searchAutoB || new BMap.Autocomplete(option);
    return new Promise((resolve, reject) => {
      let onSearchComplete = (result) => {
        this.searchAutoB.hide();
        if (result) {
          let addressList = result.Lq || result.Qq;
          let list = addressList.map((item) => {
            let address = item.province + item.city + item.district + item.street + item.business;
            return {
              title: item.business,
              address
            };
          });
          resolve(list);
        } else {
          reject(result);
        }
      };
      this.searchAutoB.setSearchCompleteCallback(onSearchComplete);
      this.searchAutoB.search(place);
    });
  },

  /**
   * 定位：包含浏览器定位和IP定位。
   * @param isIP 是否IP定位。IP定位只能获取城市
   */
  getLocation(isIP = false) {
    if (isIP) {
      //IP定位
      this.localCity = this.localCity || new BMap.LocalCity();
      return new Promise((resolve, reject) => {
        let callback = function (rs) {
          //定位成功
          if (rs) {
            resolve(rs);
          } else {
            reject(rs);
          }
        };
        this.localCity.get(callback);
      });
    } else {
      //浏览器定位
      this.geoLocation = this.geoLocation || new BMap.Geolocation();
      return new Promise((resolve, reject) => {
        let callback = function (rs) {
          //定位成功
          if (this.getStatus() === 0) {
            resolve(rs);
          } else {
            reject(rs);
          }
        };
        this.geoLocation.getCurrentPosition(callback, {enableHighAccuracy: true});
      });
    }
  },

  /**
   * 根据坐标获取详细的地址信息
   * @param point 百度地图坐标，格式：Point。
   */
  getDetail(point) {
    //逆/地址解析，用于坐标与地址间的相互转换。详情见：http://lbsyun.baidu.com/cms/jsapi/reference/jsapi_reference.html#a7b27
    this.geoc = this.geoc || new BMap.Geocoder();

    return new Promise((resolve, reject) => {
      this.geoc.getLocation(point, (rs) => {
        if (rs.addressComponents) {
          let data = {point};
          let {province, city, district} = rs.addressComponents;
          data.province = province;
          data.city = city;
          data.district = district;
          resolve(data);
        } else {
          reject(rs);
        }
      });
    });
  },

  /**
   * 添加点覆盖物
   * @param map 百度地图控件
   * @param point 添加点
   * @param config 配置，格式：MarkerOptions
   * {
   *   iconOption,//标注所用的图标对象，格式：
   *   {
   *     url,//图片，格式：String。注意：使用import方式的图片，本质上还是图片地址。
   *     size,//图标大小，格式：[x,y]。默认：[22, 36]。
   *     opts,//图片配置，格式：IconOptions
   *   }
   *   offsetOption,//标注的位置偏移值，格式:[x,y]。默认：[0, -18]。
   *   rotation,//旋转角度，格式：Number
   *   title,//鼠标移到marker上的显示内容，格式：String
   * }
   */
  addPoint(map, point, config = {}) {
    if (!map || !point) return;

    let {iconOption = {}, offsetOption, ...other} = config;
    let {url, size = [22, 36], opts} = iconOption;

    let marker = null;
    if (url) {
      let bSize = new BMap.Size(size[0], size[1]);
      let icon = new BMap.Icon(url, bSize, opts);
      icon.setImageSize(bSize);

      offsetOption = offsetOption || [0, -size[1] / 2];
      let offset = new BMap.Size(offsetOption[0], offsetOption[1]);

      //创建标注。使用自定义图标时，标注的地理坐标点将位于标注所用图标的中心位置。
      marker = new BMap.Marker(point, {offset, icon, ...other});
    } else {
      marker = new BMap.Marker(point, {...other});
    }

    map.addOverlay(marker);
    return marker;
  },

  /**
   * 添加点覆盖物：海量点
   * 参考：http://lbsyun.baidu.com/jsdemo.htm#c1_19
   * @param map 百度地图控件
   * @param config 配置。类型：
   * {
   *  pointList，//点列表。类型：[Point]
   *  dataList，//数据列表。类型：[{title}]。注意：dataList与pointList的数据一一对应。
   *  options,//点覆盖物样式。类型：{size,shape,color}
   *  onClick,//鼠标点击事件
   *  onMouseOver,//鼠标移入事件
   *  onMouseOut,//鼠标移出事件
   * }
   */
  addPointMass(map, config = {}) {
    if (!map) return;
    // 判断当前浏览器是否支持绘制海量点
    if (!document.createElement('canvas').getContext) {
      window.alert('请在chrome、safari、IE8+以上浏览器查看本示例');
      return;
    }

    let that = this;
    if (!this.onMouseMass) {
      /**
       * 默认的鼠标移入、移出事件
       * @param e 鼠标移入、移出事件。类型：{type, target,point}
       * @param type 事件类型，1代表移入，2代表移出。类型：1|2。默认值：1。
       */
      this.onMouseMass = function (e, type = 1) {
        let index = config.pointList && config.pointList.findIndex((i) => i.equals(e.point));
        //点覆盖物对应的数据
        let data = config.dataList && config.dataList[index];
        if (data && data.title) {
          this.massLabels = this.massLabels || [];
          if (type === 1) {
            this.massLabels[index] = new BMap.Label(data.title, {position: e.point}); // 创建文本标注对象
            map.addOverlay(this.massLabels[index]);
          } else {
            map.removeOverlay(this.massLabels[index]);
          }
        }
      };
    }

    let {
      pointList,
      options,
      onClick,
      onMouseOver = (e) => this.onMouseMass(e, 1),
      onMouseOut = (e) => this.onMouseMass(e, 2)
    } = config;
    // 初始化PointCollection
    let pointCollection = new BMap.PointCollection(pointList, options);
    pointCollection.addEventListener('click', onClick);
    pointCollection.addEventListener('mouseover', onMouseOver);
    pointCollection.addEventListener('mouseout', onMouseOut);
    map.addOverlay(pointCollection);

    return pointCollection;
  },

  /**
   * 移除点覆盖物
   * @param map 百度地图控件
   * @param point 需要移除的点，格式：Point。
   */
  removePoint(map, point) {
    if (!map || !point) return;

    map.getOverlays().some((item) => {
      if (point.equals(item.getPosition())) {
        map.removeOverlay(item);
        return true;
      }
      return false;
    });
  },

  /**
   * 添加折线覆盖物
   * @param map 百度地图控件
   * @param points 折线上的点，格式:[Point]|Point数组字符串。Point为百度经纬度坐标。
   * @param config 配置，格式：
   * {
   *  isViewport,//是否调整视野，格式：Boolean
   *  options,//折线配置，格式：PolylineOptions
   * }
   */
  addLine(map, points, config = {}) {
    if (!map || !points) return;

    if (points instanceof Array) {
      //将points的元素转为Point。
      points = points.map((item) => this.getPoint(item));
    }

    let {isViewport, options = {}} = config;
    let {strokeColor = color.line, strokeWeight = 6, ...other} = options;
    let param = {strokeColor, strokeWeight, ...other};
    let poly = new BMap.Polyline(points, param);
    map.addOverlay(poly);

    //调整视野
    isViewport && map.setViewport(poly.getPath());
    return poly;
  },

  /**
   * 添加多边形覆盖物
   * @param map 百度地图控件
   * @param points 多边形上的点，格式:[Point]|Point数组字符串。Point为百度经纬度坐标。
   * @param config 配置，格式：
   * {
   * isViewport,//是否调整视野，格式：Boolean
   * options,//多线形配置，格式：PolygonOptions
   * }
   */
  addPolygon(map, points, config = {}) {
    if (!map || !points) return;

    if (points instanceof Array) {
      //将points的元素转为Point。
      points = points.map((item) => this.getPoint(item));
    }

    let {isViewport, options = {}} = config;
    let {
      fillColor = color.polygonFill,
      fillOpacity = 0.5,
      strokeColor = color.polygonStroke,
      strokeWeight = 2,
      strokeStyle = 'dashed',
      ...other
    } = options;
    let param = {fillColor, fillOpacity, strokeColor, strokeWeight, strokeStyle, ...other};
    let poly = new BMap.Polygon(points, param);
    map.addOverlay(poly);

    //调整视野
    isViewport && map.setViewport(poly.getPath());
    return poly;
  },

  /**
   * 添加多边形的外部区域：即多边形的四个角
   * 参考：https://blog.csdn.net/C_fashionCat/article/details/90749443
   * @param map 百度地图控件
   * @param points 多边形上的点，格式:[Point]|Point数组字符串。Point为百度经纬度坐标。
   * @param config 配置，格式：
   * {
   *  options,//多边形配置，格式：PolygonOptions
   * }
   */
  addPolygonOuter(map, points, config = {}) {
    if (!map || !points) return;

    const EN_JW = '180, 90;';
    const NW_JW = '-180, 90;';
    const WS_JW = '-180, -90;';
    const SE_JW = '180, -90;';

    let {options = {}} = config;
    let {fillColor = color.polygonFill, fillOpacity = 1, strokeColor = 'none', strokeOpacity = 1, ...other} = options;
    let param = {fillColor, fillOpacity, strokeColor, strokeOpacity, ...other};
    //将Point数组转为字符串
    if (typeof points !== 'string') {
      points = points.map((item) => item.lng + ', ' + item.lat);
      points = points.join(';');
    }
    points = points + SE_JW + SE_JW + WS_JW + NW_JW + EN_JW + SE_JW;

    // 东南西北四个角添加一个覆盖物
    let poly = new BMap.Polygon(points, param);
    map.addOverlay(poly);
    return poly;
  },

  /**
   * 画行政区域边界
   * @param map 百度地图控件
   * @param name 行政区域名称
   * @param config 配置，格式：
   * {
   * isViewport,//是否调整视野，格式：Boolean
   * minZoom,//最小缩放级别，格式：Number。默认为6。
   * showOuter,//是否绘制外围区域，格式：Boolean
   * options,//多线形配置，格式：PolygonOptions
   * optionsOuter,//外围多线形配置，格式：PolygonOptions
   * }
   */
  addBoundary(map, name, config = {}) {
    if (!map || !name) return Promise.resolve();

    //行政区域的边界
    this.boundary = this.boundary || new BMap.Boundary();
    let {isViewport = true, minZoom = 6, showOuter = true, options = {}, optionsOuter} = config;
    let {fillColor = 'transparent', ...other} = options;

    return new Promise((resolve, reject) => {
      let addPolygon = this.addPolygon;
      let addPolygonOuter = this.addPolygonOuter;
      this.boundary.get(name, function (rs) {
        if (rs && rs.boundaries && rs.boundaries.length > 0) {
          let points;
          //查询结果，会返回多个点集合字符串，我们取最长的点集合字符串
          for (let item of rs.boundaries) {
            if (!points || points.length < item.length) {
              points = item;
            }
          }
          let poly = addPolygon(map, points, {isViewport, options: {fillColor, ...other}});
          let polyOuter = showOuter && addPolygonOuter(map, points, {options: optionsOuter});
          map.setMinZoom(minZoom);

          resolve({poly, polyOuter});
        } else {
          map.centerAndZoom(name);
          reject(rs);
        }
      });
    });
  },

  /**
   * 计算两点所在有向线段顺时针方向旋转的角度
   * @param start 起点,格式:Pixel或Point
   * @param end 起点,格式:Pixel或Point
   * @param map 百度地图
   */
  angleCalculation(start, end, map) {
    if (!start || !end) return 0;

    //将百度经纬度坐标转为百度平面坐标
    if (start instanceof BMap.Point) {
      start = map.pointToPixel(start);
    }
    if (end instanceof BMap.Point) {
      end = map.pointToPixel(end);
    }

    let tan = (end.y - start.y) / (end.x - start.x);
    //将tan值转为角度
    let deg = (Math.atan(tan) * 180) / Math.PI;
    //计算顺时针方向旋转的角度
    if (end.x >= start.x) {
      deg = 270 - deg;
    } else if (end.x < start.x) {
      deg = 90 - deg;
    }
    return deg;
  },

  /**
   * 生成静态图
   * 参考文档：http://lbsyun.baidu.com/index.php?title=static
   * @param config 配置
   * {
   *  center      //中心点，格式：Point。默认为ploy的中心点。
   *  width       //宽度，格式：Number。默认为200。
   *  height      //高度，格式：Number。默认为200。
   *  zoom        //缩放级别，格式：Number。默认为11。
   *  ploy        //多边形，格式：Polygon。
   *  pathStyles  //多边形的配置，格式：{color,weight,opacity,fillColor}
   * }
   */
  getStaticImg(config = {}) {
    let {center, width = 400, height = 300, zoom = 11, poly, ployOption = {}} = config;
    if (poly) {
      if (!center) {
        //调整视野
        poly.getMap().setViewport(poly.getPath());
        center = poly.getMap().getCenter();
        //为了保证生成完整的图片，将缩放级别降低
        zoom = poly.getMap().getZoom() - 1;
      }
    }
    //静态图地址
    let url = 'https://api.map.baidu.com/staticimage/v2?ak=' + this.ak;
    //设置中心点
    url = url + `&center=${center.lng},${center.lat}`;
    //设置宽高、缩放级别
    url = url + `&width=${width}&height=${height}&zoom=${zoom}`;
    //设置多边形
    if (poly) {
      //设置多边形路径
      let strPoly = poly.getPath().map((item) => item.lng + ',' + item.lat);
      url = url + '&paths=' + strPoly.join(';');
      let {
        color = '0xB0B0AF', //折线颜色， 16进制颜色表示。
        weight = 3, //折线粗细，3到32之间的正整数。
        opacity = 0.5, //折线或者面的透明度0，1之间的一个小数。
        fillColor = '0xFFFFFF' //填充颜色；可选参数!注意!：如果设置了填充颜色则折线自动闭合为多边形。
      } = ployOption;
      //设置多边形样式
      let strOption = `&pathStyles=${color},${weight},${opacity},${fillColor}`;
      url = url + strOption;
    }
    return url;
  }
};

/**
 * 个性化定制地图样式。
 * 在线定制：http://lbsyun.baidu.com/custom/
 */
const BDStyle = [
  {
    featureType: 'background',
    elementType: 'all',
    stylers: {}
  }
];

export {BDStyle};
export default MapTool;
